package com.leap.config;

import com.leap.entity.Account;
import com.leap.entity.RestBean;
import com.leap.entity.vo.AdminVO;
import com.leap.entity.vo.AuthorizeVO;
import com.leap.filter.JwtAuthorizeFilter;
import com.leap.mini.AdminAuthenticationProvider;
import com.leap.mini.MiniConfigurer;
import com.leap.service.AccountService;
import com.leap.service.ArticleService;
import com.leap.utils.JwtUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import java.io.IOException;
import java.io.PrintWriter;

@Configuration
public class SecurityConfiguration {

    @Autowired
    AccountService accountService;

    @Autowired
    private ArticleService articleService;

    @Autowired
    JwtUtils utils;

    @Autowired
    JwtAuthorizeFilter jwtAuthorizeFilter;

    @Bean
    public SecurityFilterChain FilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(conf -> conf
                        .requestMatchers("/api/auth/**","/error",
                                "/doc.html",            // Knife4j 主界面
                                "/webjars/**",          // Knife4j 的静态资源（JS/CSS）
                                "/v3/**",               // OpenAPI 的接口文档数据
                                "/swagger-resources/**", // Swagger 资源配置
                                "/favicon.ico",          // 网站图标（避免 403 错误）
                                "/swagger-ui/**"
                        ).permitAll()
                        .requestMatchers("/api/admin/**").hasAnyRole("admin")
                        .anyRequest().authenticated()
                )
                .formLogin(conf -> conf
                        .loginProcessingUrl("/api/auth/adminLogin")
                        .failureHandler(this::onAuthenticationFailure)
                        .successHandler(this::onAdminAuthenticationSuccess)
                )
                .with(new MiniConfigurer<>(), miniLogin -> miniLogin
                        .successAuthenticationHandler(this::onAuthenticationSuccess)
                        .failureAuthenticationHandler(this::onAuthenticationFailure)
                )
                .logout(conf -> conf
                        .logoutUrl("/api/auth/logout")
                        .logoutSuccessHandler(this::onLogoutSuccess)
                )
                .exceptionHandling(conf -> conf
                        .authenticationEntryPoint(this::onUnauthorized)
                        .accessDeniedHandler(this::onAccessDeny)
                )
                .csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(conf -> conf
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                )
                .addFilterBefore(jwtAuthorizeFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    private void onAdminAuthenticationSuccess(HttpServletRequest request,
                                              HttpServletResponse response,
                                              Authentication authentication) throws IOException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        String adminName = authentication.getPrincipal().toString();
        String adminRole = authentication.getAuthorities().toString();
        accountService.loadCategoryName();
        articleService.refreshArticleSumCache();
        String token = utils.createAdminJwt(adminName, adminRole);
        AdminVO adminVO = AdminVO.builder()
                .username(adminName)
                .token(token)
                .expire(utils.expireTime())
                .role(adminRole)
                .build();

        response.getWriter().write(RestBean.success(adminVO).asJsonString());

    }

    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        String openid = authentication.getPrincipal().toString();
        Account account = accountService.selectByOpenId(openid);
        accountService.loadCategoryViewCache(account.getId());
        accountService.loadLikeListCache(account.getId());
        accountService.loadCategoryName();
        articleService.refreshArticleSumCache();
        String token = utils.createJwt(account.getId(), account.getOpenid());
        AuthorizeVO vo = AuthorizeVO.builder()
                .token(token)
                .expire(utils.expireTime())
                .build();
        response.getWriter().write(RestBean.success(vo).asJsonString());
    }

    public void onLogoutSuccess(HttpServletRequest request,
                                HttpServletResponse response,
                                Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        PrintWriter writer = response.getWriter();
        String authorization = request.getHeader("Authorization");
        if (utils.invalidateJwt(authorization)){
            writer.write(RestBean.success().asJsonString());
        }else {
            writer.write(RestBean.failure(400, "退出登录失败").asJsonString());
        }
    }

    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response,
                                        AuthenticationException exception) throws IOException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(RestBean.unauthorized(exception.getMessage()).asJsonString());
    }

    public void onUnauthorized(HttpServletRequest request,
                               HttpServletResponse response,
                               AuthenticationException exception) throws IOException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(RestBean.unauthorized(exception.getMessage()).asJsonString());
    }

    public void onAccessDeny(HttpServletRequest request,
                             HttpServletResponse response,
                             AccessDeniedException exception) throws IOException{
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(RestBean.forbidden(exception.getMessage()).asJsonString());
    }

}
